home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Magazin/MacEasy 21
/
Mac Magazin and MacEasy Magazine CD - Issue 21.iso
/
Wissenschaft & Technik
/
yorick12vr1-ppc folder
/
include
/
fits.i
< prev
next >
Wrap
Text File
|
1996-02-21
|
26KB
|
878 lines
/*
* fits.i --
*
* Routines to manipulate FITS files (IAU astronomical data format).
* Provides functions:
* - fitsAddComment: add comments in a FITS header;
* - fitsAddHistory: add history records in a FITS header;
* - fitsBitpix: compute bit per pixel;
* - fitsFixHeader: check/fix FITS header;
* - fitsHeader: provides default FITS header,
* - fitsRead: read FITS file;
* - fitsRescale: rescale data into integer array;
* - fitsWrite: write FITS file.
*
* $Id: fits.i,v 1.5 1996/02/21 10:55:30 eric Exp $
*
* Copyright (c) 1996, Eric THIEBAUT (thiebaut@obs.univ-lyon1.fr, Centre de
* Recherche Astrophysique de Lyon, 9 avenue Charles Andre, F-69561 Saint
* Genis Laval Cedex).
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details (to receive a copy of the GNU
* General Public License, write to the Free Software Foundation, Inc., 675
* Mass Ave, Cambridge, MA 02139, USA).
*/
/*
* History:
* Known bugs in release 1.1:
* - sometimes fails to compile with Yorick-1.1a on a HP workstation;
* - bugs in `fitsWrite'.
*
* 02/16/96 Eric THIEBAUT:
* - fixed bugs in `fitsWrite' when array is of integer type;
* - added some functions and cleanup the code;
* - removed spurious debug messages;
* - seems to properly compile with Yorick-1.2.
*
* 02/16/96 release 1.3.
*
* 02/17/96 Eric THIEBAUT:
* - when rescaling in fitsRead, bitpix=32 -> double, otherwise float;
* - fitsRead: set file primitive to be that of a SUN (most significant
* byte first) to follow FITS standard and make data portable (that
* was already done in fitsWrite and was fixed by Dave in its 1.2
* release...);
* - fitsRead: by default ignores zero *AND* unit dimensions, this
* was not what the documentation pretends; fix it to follow
* documentation;
* - incorporates most of the changes made by David Munro.
*
* 02/17/96 release 1.4.
*
* 02/21/96 Eric THIEBAUT:
* - in fitsRead: speed-up parsing of FITS header (a side effect is
* that the maximum number of dimensions is 33);
* - in fitsWrite and _fitsPut...: use extern variables to simplify
* (and speed-up?) the writting of the header;
*
* 02/21/96 release 1.5.
*
* Known bugs in release 1.5:
* - Even if fits_max_naxis is changed, the maximum number of
* dimensions is 33. I do not think that this is a limitation
* thus I won't fix this ;-).
*/
require, "string.i";
/*----------------------------------------------------------------------*/
local fits_max_naxis;
/* DOCUMENT fits_max_naxis -- maximum number of allowed axis in FITS
Its value should *NOT* be modified from a Yorick session.
*/
fits_max_naxis= 9; /* if you need more axis, change this value in this
* file and source it again. */
struct FitsHeader {
int bitpix; // 8: pixels are unsigned bytes
// 16: pixels are signed 2-byte integers
// 32: pixels are signed 4-byte integers
// -32: pixels are 4-byte floating points
// -64: pixels are 8-byte floating points
// -128: pixels are 2x8-byte complex
int naxis; // number of axis
int axis(fits_max_naxis); // number of pixel along axis No. n
// Pixel intensity / brighness:
double bscale; // pixelValue = BZERO+BSCALE*fileValue
double bzero; // pixelValue = BZERO+BSCALE*fileValue
string bunit; // brightness unit
double datamax; // maximum data value in the file
double datamin; // minimum data value in the file
// Miscellaneous information:
string object; // image name
string date; // date of file creation (dd/mm/yy)
string date_obs; // date of data acquisition (dd/mm/yy)
string origin; // institution
string instrume; // data acquisition instrument
string telescop; // data acquisition telescope
string observer; // observer name/identification
string history; // newline separated history lines
string comment; // newline separated comment lines
// coordinate system for each axis:
double epoch; // epoch of coordinate system (year)
double crval(fits_max_naxis); // coord = CRVAL+(pixel-CRPIX)*CDELT
double crpix(fits_max_naxis); // coord = CRVAL+(pixel-CRPIX)*CDELT
double cdelt(fits_max_naxis); // coord = CRVAL+(pixel-CRPIX)*CDELT
string ctype(fits_max_naxis); // type of physical coordinate
double crota(fits_max_naxis); // rotation angle of axis No. #
}
/*----------------------------------------------------------------------*/
func fitsHeader(&header)
/* DOCUMENT header= fitsHeader();
fitsHeader, header;
Creates a structure FitsHeader with defaults. Equivalent to:
header= FitsHeader(bscale= 1., bzero= 0.);
SEE ALSO: fitsRead, fitsWrite.
*/
{
return (header= FitsHeader(bscale= 1., bzero= 0.));
}
func fitsFixHeader(&header)
/* DOCUMENT fitsFixHeader, header
Check consistency of FITS header.
SEE ALSO: fitsRead, fitsWrite.
*/
{
if (structof(header) != FitsHeader)
error, "bad type for HEADER (not FitsHeader)";
if (header.datamin > header.datamax)
error, "DATAMIN greater than DATAMAX in FITS header!";
if (header.bscale == 0. && header.bzero != 0.) {
write, "WARNING: BZERO ignored because BSCALE is zero";
header.bzero= 0.;
}
}
func fitsAddComment(&header, str)
/* DOCUMENT fitsAddComment, header, string
Add STRING as a comment in HEADER (a FitsHeader structure). If
STRING is too long, it is broken in pieces.
SEE ALSO: fitsRead, fitsWrite.
*/
{
str= strtrim(str); // remove trailing/leading blanks
while (strlen(str)) {
header.comment += strpart(str, :69)+"\n";
str= strtrim(strpart(str, 70:), 1);
}
}
func fitsAddHistory(&header, str, stamp=)
/* DOCUMENT fitsAddHistory, header, string
Add STRING as an history card in HEADER (a FitsHeader structure).
If STRING is too long, it is broken in pieces. Keyword STAMP may
be used with a zero value to avoid the additional time stamp
(default is to add a time stamp).
SEE ALSO: fitsRead, fitsWrite.
*/
{
str= strtrim(str); // remove trailing/leading blanks
if (is_void(stamp))
stamp= 1N; // default is to add a time stamp
if (stamp) {
str= getdate()+" "+gettime()+": "+str;
indent= " ";
} else {
indent= "";
}
len= 69 - strlen(indent);
header.history += strpart(str, :69)+"\n";
str= strtrim(strpart(str, 70:), 1);
while (strlen(str)) {
header.history += indent + strpart(str, :len) + "\n";
str= strtrim(strpart(str, len+1:), 1);
}
}
func fitsBitpix(data)
/* DOCUMENT fitsBitpix(data)
Return bits per pixel for array data.
SEE ALSO: fitsWrite.
*/
{
s= structof(data);
if (s == char)
return 8 * sizeof(char);
if (s == short)
return 8 * sizeof(short);
if (s == int)
return 8 * sizeof(int);
if (s == long)
return 8 * sizeof(long);
if (s == float)
return -8 * sizeof(float);
if (s == double)
return -8 * sizeof(double);
if (s == complex) {
write, "WARNING: using BITPIX=-128 is not standard FITS";
return -8 * sizeof(complex);
}
error, "bad data type for DATA";
}
/*----------------------------------------------------------------------*/
func fitsRescale(data, bitpix, &bscale, &bzero, data_min=, data_max=)
/* DOCUMENT rescaled_data= fitsRescale(data, bitpix, bscale, bzero)
Linearly rescale the values of input array DATA to fit into
integers with BITPIX bits per value (i.e., `char', `short' or
`long' for BITPIX being 8, 16 and 32 respectively).
Arguments BSCALE and BZERO are optional and purely outputs passed
by address. Their value will be set so that:
DATA(i) = BZERO + BSCALE * RESCALED_DATA(i)
where the equality may not be exact due to rounding errors. The
difference is however the smallest possible, i.e., less than
BSCALE / 2.
Keywords DATA_MIN and DATA_MAX may be used to supply the maximum
and minimum data values or to set brightness cutoffs.
SEE ALSO fitsRead, fitsWrite.
*/
{
if (bitpix == 8) {
// assume ``char'' is 8 bits unsigned
file_type= char;
file_unsigned= 1N;
} else if (bitpix == 16) {
// assume ``short'' is 16 bits signed
file_type= short;
file_unsigned= 0N;
} else if (bitpix == 32) {
// assume ``long'' is 32 bits signed
file_type= long;
file_unsigned= 0N;
} else {
error, "bad BITPIX (should be 8, 16 or 32)";
}
data_min= double((is_void(data_min) ? min(data) : data_min));
data_max= double((is_void(data_max) ? max(data) : data_max));
if (data_max < data_min)
error, "bad DATA_MAX and DATA_MIN";
if (file_unsigned) {
file_min= 0.;
file_max= 2.^bitpix - 1.;
} else {
file_min= -2.^(bitpix - 1);
file_max= 2.^(bitpix - 1) - 1.;
}
if (data_max == data_min) {
bzero= data_min;
return array(file_type(0), dimsof(data));
}
bscale= (data_max - data_min) / (file_max - file_min);
bzero= data_min - bscale * file_min;
return file_type(min(file_max, max(file_min, (data - bzero) / bscale)));
}
/*----------------------------------------------------------------------*/
func fitsWrite(name, data, header, rescale=, pack=)
/* DOCUMENT fitsWrite, filename, data, header
Write DATA in file FILENAME using FITS format. If present, the
information of the optional argument HEADER (a FitsHeader
structure) will be used to write the FITS file header.
Keyword PACK, if non-nil and non-zero, indicates that axis whith
unit dimension should be ignored. The default is to ignore only
zero length axis.
Keyword RESCALE, if non-nil and zero, indicates that input data
values should not be rescaled into integers according to FITS
standard. The default, is to recode doubles as 32 bit integers,
floats as 16 bit integers and integers into smallest integers if it
is possible without loss of dynamic (e.g., an array of long
integers ranging from -31 to +130 will be recoded as chars).
SEE ALSO: fitsRead, fitsRescale, fitsAddHistory, fitsAddComment.
*/
{
local bitpix, bzero, bscale;
/*
* Check input data.
*/
if (is_void(data))
error, "empty DATA array!!!";
if (dimsof(data)(1) > fits_max_naxis) {
write, swrite(format="*** Currently, number of dimensions for "+
"FITS cannot exceed %d\n*** If you really need "+
"more dimensions, you'll have to modify the\n"+
"*** source file...", int(fits_max_naxis));
error, "too many dimensions";
}
bitpix= fitsBitpix(data); // also make sure that data type is valid
/*
* Minimal header.
*/
if (is_void(header)) {
fitsHeader, header; // create a FITS header with defaults
} else {
fitsFixHeader, header; // just check header
}
if (strlen(header.date) == 0) {
header.date= getdate()+" "+gettime();
}
header.bitpix= bitpix;
if (header.bscale != 1. || header.bzero != 0.)
write, "WARNING: initial [BSCALE,BZERO] != [1,0]";
/*
* Rescale brightness to fit standards.
*/
if (is_void(rescale) || rescale) {
if (structof(data) == float || structof(data) == double) {
// Convert float to short and double to long
bitpix= fitsBitpix((structof(data) == float ? short(0) : long(0)));
data= fitsRescale(data, bitpix, bscale, bzero);
header.bitpix = bitpix;
header.bzero += header.bscale * bzero;
header.bscale *= bscale;
} else if (structof(data) != char) {
// Integers: check if data fit into smaller data type
data_min= double(min(data));
data_max= double(max(data));
data_dyn= long(ceil(log(data_max - data_min + 1.) / log(256.)));
if (data_dyn < sizeof(data(1))) {
if (data_dyn == sizeof(char)) {
file_type= char;
file_unsigned= 1N; // assume ``char'' is unsigned
} else {
file_type= short;
file_unsigned= 0N; // assume ``short'' is signed
}
if (file_unsigned)
bzero= data_min;
else
bzero= data_min + 2.^(8. * data_siz - 1);
data= file_type(data - bzero);
header.bitpix= fitsBitpix(file_type(0));
header.bzero += header.bscale * bzero;
}
}
}
/*
* Compute dimensions list.
*/
header.axis(*)= 0;
dims= dimsof(data);
if (dims(1) == 0) {
// data is a scalar!
header.naxis= 1;
header.axis(1)= 1;
} else {
// there is at least one dimension
dims = dims(2:);
if (pack) {
i= where(dims > 1);
if (numberof(i)) {
dims= dims(i);
} else {
dims= [1];
}
}
header.naxis = numberof(dims);
header.axis(:header.naxis) = dims;
}
/*
* Open data file.
*/
if (open(name+"L", "", 1)) {
write, format="%s", "Remove existing file "+name+"L (y or n)? ";
response= strtok(rdline(prompt=string(0)))(1);
if (response=="y" || response=="yes") {
remove, name+"L";
} else {
write, "No file written or deleted.";
return;
}
}
file= open(name, "wb");
sun_primitives, file; // Most Significant Byte First
address= 0; // Address in file
/*
* Write header.
*/
_fitsPutLogical, "SIMPLE", 1;
_fitsPutInteger, "BITPIX", header.bitpix;
_fitsPutInteger, "NAXIS", header.naxis;
for (i=1; i<=header.naxis; i++) {
_fitsPutInteger, swrite(format="NAXIS%d", i), header.axis(i);
}
if (header.bscale != 1.0 || header.bzero != 0.) {
_fitsPutReal, "BSCALE", header.bscale;
_fitsPutReal, "BZERO", header.bzero;
}
if (strlen(header.bunit))
_fitsPutString, "BUNIT", header.bunit;
if (header.datamax || header.datamin) {
_fitsPutReal, "DATAMAX", header.datamax;
_fitsPutReal, "DATAMIN", header.datamin;
}
if (strlen(header.object))
_fitsPutString, "OBJECT", header.object;
if (strlen(header.date))
_fitsPutString, "DATE", header.date;
if (strlen(header.date_obs))
_fitsPutString, "DATE-OBS", header.date_obs;
if (strlen(header.origin))
_fitsPutString, "ORIGIN", header.origin;
if (strlen(header.instrume))
_fitsPutString, "INSTRUME", header.instrume;
if (strlen(header.telescop))
_fitsPutString, "TELESCOP", header.telescop;
if (strlen(header.observer))
_fitsPutString, "OBSERVER", header.observer;
if (header.epoch)
_fitsPutReal, "EPOCH", header.epoch;
for (i=1; i<=header.naxis; i++) {
if (strlen(header.ctype(i)))
_fitsPutString, swrite(format="CTYPE%d", i), header.ctype(i);
if (header.cdelt(i)) {
_fitsPutReal, swrite(format="CRVAL%d", i), header.crval(i);
_fitsPutReal, swrite(format="CRPIX%d", i), header.crpix(i);
_fitsPutReal, swrite(format="CDELT%d", i), header.cdelt(i);
_fitsPutReal, swrite(format="CROTA%d", i), header.crota(i);
}
}
if (strlen(header.comment)) {
comment = strtok(header.comment, "\n");
while (comment(1)) {
_fitsPutComment, "COMMENT", comment(1);
comment = strtok(comment(2), "\n");
}
}
if (strlen(header.history)) {
history = strtok(header.history, "\n");
while (history(1)) {
_fitsPutComment, "HISTORY", history(1);
history = strtok(history(2), "\n");
}
}
/*
* Write END keyword and terminate current header block.
*/
_fitsPutComment, "END";
empty= array(' ', 80);
empty(0)= '\n';
while (address % 2880) {
_write, file, address, empty;
address += 80;
}
/*
* Write data array.
*/
_write, file, address, data;
close, file;
/*
* Yorick uses a Contents Log file, which should be removed...
*/
if (open(name+"L", "", 1))
remove, name+"L";
}
/*----------------------------------------------------------------------*/
func fitsRead(name, &header, which=, pack=, rescale=)
/* DOCUMENT a= fitsRead(filename, header)
Returns the data of the FITS file FILENAME. If present, the
optional argument HEADER will be used to store the contents of the
FITS header file (a FitsHeader structure).
Keyword WHICH may be used to indicate which sub-array should be
returned. For instance, if the array DATA with dimensions
(235,453,7) is stored in the FITS file "data.fits", the sub-array
DATA(,,4) can be read by:
SUB_DATA= fitsRead("data.fits", which= 4);
Keyword PACK, if non-nil and non-zero, indicates that axis whith
unit dimension should be ignored. The default is to ignore only
zero length axis.
Keyword RESCALE, if non-nil and zero, indicates that read data
values should not be rescaled according to FITS keywords BSCALE and
BZERO. The default is to rescale data values if BSCALE is not
1. or BZERO is not 0.
SEE ALSO: fitsWrite, fitsRescale.
*/
{
local axis;
fitsHeader, header; // Create FITS header with defaults
file= open(name, "rb");
sun_primitives, file; // Most Signficant Byte First
end_not_found= 1N;
n= 1;
address= 0;
do {
/*
* Read header block.
*/
buffer= array(char, 80, 36);
if (_read(file, address, buffer) != sizeof(buffer))
error, "cannot read header";
address += sizeof(buffer);
/*
* Get keywords: convert to upper case letters and remove trailing
* blanks.
*/
k= buffer(1:8,);
v= buffer(9:,);
if (numberof((i=where(k >= 'a'))) && numberof((j=where(k(i) <= 'z'))))
k(i(j))+= long('A')-long('a');
keyword= array(string, 36);
value= array(string, 36);
for (i=1; i<=36; i++) {
sread, string(&k(,i)), format="%[^ \t]", keyword(i);
value(i)= string(&v(,i));
}
k= v= buffer= [];
/*
* Parse minimal header.
*/
if (n > 1) {
i= 0;
} else {
if (keyword(1) != "SIMPLE" || !_fitsGetLogical(keyword(1), value(1)))
error, "not a standard FITS file";
if (keyword(2) != "BITPIX" ||
noneof(((header.bitpix= _fitsGetInteger(keyword(2), value(2))) ==
[8, 16, 32, 64, -32, -64, -128])))
error, "bad BITPIX or no BITPIX at line 2";
if (keyword(3) != "NAXIS" ||
(header.naxis= _fitsGetInteger(keyword(3), value(3))) < 1)
error, "bad NAXIS or no NAXIS at line 3";
if (header.naxis > fits_max_naxis) {
write, swrite(format="*** Currently, number of dimensions for "+
"FITS cannot exceed %d\n*** If you really need "+
"more dimensions, you'll have to modify the\n"+
"*** source file...", int(fits_max_naxis));
error, swrite(format="NAXIS=%d too large", header.naxis);
}
naxis= header.naxis;
for (k=1; k<=naxis; k++) {
i= k+3;
s= swrite(format="NAXIS%d", k);
if (keyword(i) != s ||
(header.axis(k)= _fitsGetInteger(keyword(i), value(i))) < 0)
error, "bad "+s;
}
n= i;
}
/*
* Parse other keywords.
*/
while (++i <= 36) {
n++;
k= keyword(i);
v= value(i);
if (strlen(k) == 0)
continue;
if (k == "END") {
end_not_found= 0N;
break;
}
if (k == "COMMENT") {
v= strtrim(v);
if (strlen(v))
header.comment += v+"\n";
continue;
}
if (k == "HISTORY") {
v= strtrim(v);
if (strlen(v))
header.history += v+"\n";
continue;
}
if (k == "BSCALE") {
header.bscale = _fitsGetReal(k, v);
continue;
}
if (k == "BZERO") {
header.bzero = _fitsGetReal(k, v);
continue;
}
if (k == "BUNIT") {
header.bunit = _fitsGetString(k, v);
continue;
}
if (k == "DATAMAX") {
header.datamax = _fitsGetReal(k, v);
continue;
}
if (k == "DATAMIN") {
header.datamin = _fitsGetReal(k, v);
continue;
}
if (k == "EPOCH") {
header.epoch = _fitsGetReal(k, v);
continue;
}
if (k == "OBJECT") {
header.object = _fitsGetString(k, v);
continue;
}
if (k == "DATE") {
header.date = _fitsGetString(k, v);
continue;
}
if (k == "DATE-OBS") {
header.date_obs = _fitsGetString(k, v);
continue;
}
if (k == "ORIGIN") {
header.origin = _fitsGetString(k, v);
continue;
}
if (k == "INSTRUME") {
header.instrume = _fitsGetString(k, v);
continue;
}
if (k == "TELESCOP") {
header.telescop = _fitsGetString(k, v);
continue;
}
if (k == "OBSERVER") {
header.observer = _fitsGetString(k, v);
continue;
}
if (k == "BLANK") {
if (strpart(v, 1:1) == "=") {
header.blank = _fitsGetInteger(k, v);
} else {
v= strtrim(v);
if (strlen(v))
write, "WARNING: discarding BLANK comment \""+v+"\"";
}
continue;
}
if (strpart(k, 1:1) == "C") {
j= long();
s= string();
if (sread(k, format="%[^0-9]%d", s, j) != 2)
continue;
if (s == "CRVAL") {
if (j < 1 || j > header.naxis)
error, "bad "+k;
header.crval(j)= _fitsGetReal(k, v);
continue;
}
if (s == "CRPIX") {
if (j < 1 || j > header.naxis)
error, "bad "+k;
header.crpix(j)= _fitsGetReal(k, v);
continue;
}
if (s == "CDELT") {
if (j < 1 || j > header.naxis)
error, "bad "+k;
header.cdelt(j)= _fitsGetReal(k, v);
continue;
}
if (s == "CROTA") {
if (j < 1 || j > header.naxis)
error, "bad "+k;
header.crota(j)= _fitsGetReal(k, v);
continue;
}
if (s == "CTYPE") {
if (j < 1 || j > header.naxis)
error, "bad "+k;
header.ctype(j)= _fitsGetString(k, v);
continue;
}
}
write, "WARNING: unknown keyword \""+k+"\"";
}
} while (end_not_found);
/*
* Get dimensions (ignore zero and, possibly, unit dimensions, keep
* unit dimensions if keyword `pack' is zero or nil):
*/
i= where(header.axis(:header.naxis) > (pack ? 1 : 0));
dims= numberof(i);
if (dims <= 0) {
write, "WARNING empty data file";
close, file;
return [];
}
grow, dims, header.axis(i);
/*
* Parse WHICH keyword for sub-array.
*/
if (is_void(which)) {
which=0;
} else {
if (numberof(which) != 1 || which != long(which))
error, "WHICH must be a scalar integer";
last= dims(0);
if (which <= 0)
which += last;
if (which > last || which < 1)
error, "WHICH out of range";
dims= dims(:-1);
dims(1) -= 1;
}
if (header.bitpix == 8) {
data = array(char, dims);
} else if (header.bitpix == 16) {
data = array(short, dims);
} else if (header.bitpix == 32) {
data = array(long, dims);
} else if (header.bitpix == -32) {
data = array(float, dims);
} else if (header.bitpix == -64) {
data = array(double, dims);
} else if (header.bitpix == -128) {
write, "WARNING: using BITPIX=-128 is not standard FITS";
data = array(complex, dims);
} else {
error, "congratulation: you have evidenced a BUG!";
}
if (which > 1)
address += (which - 1) * sizeof(data);
if (_read(file, address, data) != numberof(data))
error, "cannot read data";
close, file;
fitsFixHeader, header;
/*
* Maybe rescale pixel values
*/
if ((is_void(rescale) || rescale) &&
(header.bscale != 1. || header.bzero != 0.)) {
if (abs(header.bitpix) < 32) {
data= float(header.bzero) + float(header.bscale) * float(data);
} else {
data= header.bzero + header.bscale * data;
}
header.bitpix= fitsBitpix(data);
header.bscale= 1.;
header.bzero= 0.;
}
return data;
}
/*----------------------------------------------------------------------*/
func _fitsGetLogical(keyword, value)
{
x = string();
if (strpart(value, 1:1) == "=" &&
sread(value, format="=%s", x) == 1 && strlen(x) == 1) {
if (x == "T" || x == "t")
return 1;
if (x == "F" || x == "f")
return 0;
}
error, "bad logical value for "+keyword;
}
func _fitsGetInteger(keyword, value)
{
x = long();
if (strpart(value, 1:1) == "=" && sread(value, format="=%d", x) == 1)
return x;
error, "bad integer value for "+keyword;
}
func _fitsGetReal(keyword, value)
{
x = double();
if (strpart(value, 1:1) == "=" && sread(value, format="=%f", x) == 1)
return x;
error, "bad real value for "+keyword;
}
func _fitsGetString(keyword, value)
{
if (strpart(value, 1:1) == "=") {
/* note that this does not allow for a / delimited comment field... */
x= strtrim(strpart(value, 2:));
if (strpart(x, 1:1) == "'") {
t= string();
sread, strpart(x, 2:), format="%[^']", t;
return strtrim(t);
} else {
return x;
}
}
//error, "bad value for "+keyword;
write, "WARNING: bad string value for "+keyword;
return "";
}
/*----------------------------------------------------------------------*/
func _fitsPutValue(fmt, keyword, value, comment)
{
extern file, address;
comment= is_void(comment) ? "" : strpart(comment, :46);
line= swrite(format=fmt, keyword, value, comment);
if (strlen(line) != 80)
error, "(BUG) bad line length: \""+line+"\"";
_write, file, address, (*pointer(line))(:80);
address += 80;
}
func _fitsPutComment(keyword, comment)
{
extern file, address;
comment= is_void(comment) ? "" : strpart(comment, 1:70);
line= swrite(format="%-8s %-70s\n", keyword, comment);
_write, file, address, (*pointer(line))(:80);
address += 80;
}
func _fitsPutInteger(keyword, value, comment)
{
_fitsPutValue, "%-8s= %20d / %-46s\n", keyword, long(value), comment;
}
func _fitsPutReal(keyword, value, comment)
{
_fitsPutValue, "%-8s= %#20.13G / %-46s\n", keyword, double(value), comment;
}
func _fitsPutString(keyword, value, comment)
{
_fitsPutValue, "%-8s= '%-18s' / %-46s\n", keyword, strpart(value, :18),
comment;
}
func _fitsPutLogical(keyword, value, comment)
{
_fitsPutValue, "%-8s= %20s / %-46s\n", keyword, (value ? "T" : "F"), comment;
}
/*----------------------------------------------------------------------*/